package main

import (
	"fmt"
	"math"
)

func main() {
	meth()      //方法
	typMeth()   //非结构体类型方法
	pointMeth() //指针类型方法
}

// 方法就是一类带特殊的接受者参数的函数 (只是函数)
// 方法接收者在它自己的参数列表内，位于 func 关键字和方法名之间。
func meth() {
	v := Vertex_Meth{3, 4}
	fmt.Println(v.Abs())
}

type Vertex_Meth struct {
	X, Y float64
}

func (v Vertex_Meth) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

//===========================================================

// 你也可以为非结构体类型声明方法。
// 你只能为在同一个包中定义的接收者类型声明方法，而不能为其它别的包中定义的类型 （包括 int 之类的内置类型）声明方法。
func typMeth() {
	f := Myfloat(-math.Sqrt2)
	fmt.Println(f.Abs())
}

type Myfloat float64

func (f Myfloat) Abs() float64 {
	if f < 0 {
		return float64(-f)
	}
	return float64(f)
}

//============================================================================================

// 你可以为指针类型的接收者声明方法。
// 这意味着对于某类型 T，接收者的类型可以用 *T 的文法。 （此外，T 本身不能是指针，比如不能是 *int)
func pointMeth() {
	v := Vertex_Point{3, 4}
	v.Scale(10)
	fmt.Println(v.Abs())
}

type Vertex_Point struct {
	X, Y float64
}

func (v Vertex_Point) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

// 指针接收者的方法可以修改接收者指向的值（如这里的 Scale 所示）。 由于方法经常需要修改它的接收者，指针接收者比值接收者更常用
// 若使用值接收者，那么 Scale 方法会对原始 Vertex 值的副本进行操作。（对于函数的其它参数也是如此。）Scale 方法必须用指针接收者来更改 main 函数中声明的 Vertex 的值。
func (v *Vertex_Point) Scale(f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}
